﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

namespace StockLearner
{
    class NNAgent: Agent
    {
        NeuralNetwork net;
        String m_policyName = "policyANN.dat";

        double rewardSum;
        TextWriter tw;

        public NNAgent():base()
        {
            tw = new StreamWriter("nn.txt");
            /*if (System.IO.File.Exists(m_policyName))
            {
                BinaryFormatter l_formatter = new BinaryFormatter();
                FileStream l_file = new FileStream(m_policyName, FileMode.Open, FileAccess.ReadWrite);
                StreamReader l_reader = new StreamReader(l_file);
                try
                {
                    net = (NeuralNetwork)l_formatter.Deserialize(l_reader.BaseStream);
                }
                catch
                {
                    net = new NeuralNetwork(30, 10, 1, 0.4);
                }
            }
            else
            {
            */
                net = new NeuralNetwork(30, 10, 1, 0.00001);
            //}
        }

        // returns list of one point that is the prediction for in 30 minutes
        public override List<YahooDataPoint> act(List<YahooDataPoint> p_state)
        {
            List<Double> ret = net.input(pointsToDoubles(p_state));
            //double[] ret = net.input(pointsToDouble(p_state));
            YahooDataPoint point = p_state[p_state.Count - 1];
            point.Close = ret[0];
            Trace.WriteLine(point.Close);

            //DateTime t = Utils.UnixTimeStampToDateTime(point.Timestamp);
            //t = t.AddMinutes(30);
            //point.Timestamp = (int)Utils.DateTimeToUnixTimestamp(t);
            point.Timestamp = Utils.AddMinutesUnixTimeStamp(point.Timestamp, 30.0);

            List<YahooDataPoint> list = new List<YahooDataPoint>();
            list.Add(point);
            return list;
        }

        public override void processReward(List<YahooDataPoint> my_prediction, List<YahooDataPoint> state, List<YahooDataPoint> result)
        {
            Double normalizationVal = state[0].Close;
            List<double> inputs = pointsToDoubles(Form1.GeneralizeAndDiscretize(state));
            List<YahooDataPoint> l = Form1.GeneralizeAndDiscretize(result);
            List<double> outputs = pointsToDoubles(Form1.GeneralizeAndDiscretize(result).Skip(29).Take(1).ToList());
            //outputs[0] -= 0.5;
            //outputs.Add(result[result.Count - 1].Close / normalizationVal);
            if (outputs[0] > 2)
            {
                Trace.WriteLine("expected value below 2");
                return;
            }
            double ret = net.train(inputs, outputs);

            rewardSum += ret;
            //tw.WriteLine(ret);
            //double[] inputs = pointsToDoublea(state);
            //double[] target = pointsToDouble(result);
            //d/ouble[] target2 = new double[1];
            //target2[0] = target.Last();
            //tw.WriteLine(net.train(inputs, target2));
        }

        public List<double> pointsToDoubles(List<YahooDataPoint> list)
        {
            List<double> ret = new List<double>();
            for (int i = 0; i < list.Count; i++)
            {
                ret.Add(list[i].Close);
            }
            return ret;
        }

        public override void saveKnowledge()
        {
            tw.Close();
            return;
            BinaryFormatter l_formatter = new BinaryFormatter();

            FileStream l_file = new FileStream(m_policyName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamWriter l_writer = new StreamWriter(l_file);

            l_formatter.Serialize(l_writer.BaseStream, net);
        }

        public void resetRewardSum()
        {
            rewardSum = 0;
        }

        public double getRewardSum()
        {
            return rewardSum;
        }
    }
}
